package com.jtw.ws;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jtw.constant.ChatModel;
import com.jtw.constant.IM_CMD;
import com.jtw.constant.IM_Message;
import com.jtw.constant.IM_Room;
import com.jtw.sys.constants.Constant;
import com.jtw.sys.model.user.TSysUser;
import com.jtw.sys.service.user.SysUserServiceImpl;
import com.jtw.sys.vo.user.SysUserInfoBaseVo;
import lombok.Data;
import org.apache.commons.lang.math.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.http.common.HttpRequest;
import org.tio.http.common.HttpResponse;
import org.tio.utils.lock.SetWithLock;
import org.tio.websocket.common.WsRequest;
import org.tio.websocket.common.WsResponse;
import org.tio.websocket.server.handler.IWsMsgHandler;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.util.*;

/**
 * DESCRIPT:
 *
 * @author cjsky666
 * @date 2019/3/22 17:07
 */
@Component
public class WsHandler implements IWsMsgHandler {

    @Autowired
    private SysUserServiceImpl sysUserServiceImpl;

    private int serviceIndex = 0;

    public static final IM_Message HEARTBEAT_PACKAGE = new  IM_Message();
    private ObjectMapper objectMapper = new ObjectMapper();
    private static IM_Room im_room =   new IM_Room();
    public static Map<String,IM_Room> roomMap= new HashMap();
    static{
        try {
            //此段代码linux 主机中若没有配置 domain的地址回环主机名称会报错
//          //错误代码如下 java.net.UnknownHostException: qyi-5ca777d18b30d: qyi-5ca777d18b30d: Name or service not known
            //解决办法 1、不使用这个代码
//                    2、 在linux服务器的host中增加一个host 名称映射 qyi-5ca777d18b30d: qyi-5ca777d18b30d 然后重启服务
            System.out.println("当前服务器群组websocket的id是"+Inet4Address.getLocalHost().getHostAddress());
            im_room.setGroupId("s1");
            im_room.setCreateTime(new Date());
            im_room.setUserId("s"+1);
            im_room.setName("客服大厅");
            im_room.setNickName("1号客服");
            roomMap.put("s1",im_room);
//            IM_Room im_room1 = new IM_Room();
//            im_room1.setGroupId("s2");
//            im_room1.setCreateTime(new Date());
//            im_room1.setUserId("s"+2);
//            im_room1.setName("客服大厅");
//            im_room1.setNickName("2号客服");
//            roomMap.put("s2",im_room1);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

    public HttpResponse handshake(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {
        if(StringUtils.isBlank(httpRequest.getParam("id"))){
            return null;
        }

        SysUserInfoBaseVo sysUserInfoBaseVo = sysUserServiceImpl.getUserInfoBaseVoByUserId(Long.valueOf(httpRequest.getParam("id").substring(1, httpRequest.getParam("id").length())));
//        System.out.println("查到的用户是"+sysUserInfoBaseVo);
        channelContext.setAttribute(Constant.KEY_USER_INFO,sysUserInfoBaseVo);
        channelContext.setAttribute(Constant.KEY_USER_USER_NAME,sysUserInfoBaseVo.getUserName());

        /**
         * 自动绑定到client组
         */
        if(httpRequest.getParam("id").startsWith("c")){
            Tio.bindGroup(channelContext,"c");
//            //      握手完成自动，绑定通道到对应选择的客服房间
//            if(StringUtils.isBlank(httpRequest.getParam("roomId"))){
//                return null;
//            }
//            if(roomMap.get(httpRequest.getParam("roomId"))==null){
//                return null;
//            }
        }
        /**
         * 自动绑定到server组
         */
        if(httpRequest.getParam("id").startsWith("s")){
            Tio.bindGroup(channelContext,"s");
        }
        Tio.bindGroup(channelContext,httpRequest.getParam("roomId"));//绑定当前用户通道到客服组
        Tio.bindUser(channelContext,httpRequest.getParam("id"));//握手请求被允许，根据id绑定当前的用户通道

        return httpResponse;
    }

    /**
     * 完成握手之后是否需要发消息
     * @param httpRequest
     * @param httpResponse
     * @param channelContext
     * @throws Exception
     */
    @Override
    public void onAfterHandshaked(HttpRequest httpRequest, HttpResponse httpResponse, ChannelContext channelContext) throws Exception {

        WsResponse wsResponse = WsResponse.fromBytes((IM_CMD.HANDSHAKE_RESP.getType().toString()).getBytes(TioWsConfig.CHARSET));
        Tio.send(channelContext,wsResponse);
        if(channelContext.userid.startsWith("c")){
            /**
             * 通知超级管理员新用户上线，刷新超级管理员用户在线列表
             */
            ChatModel model = new ChatModel();
            model.setState(1);
            model.setSid(channelContext.userid);
            model.setSname((String) channelContext.getAttribute(Constant.KEY_USER_USER_NAME));
            byte [] bytes = createBytes(IM_CMD.USER_ONLINE_NTY.getType(),model,null);
            Tio.sendToUser(channelContext.groupContext,im_room.getUserId(),WsResponse.fromBytes(bytes));
        }
    }


    @Override
    public Object onClose(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
//        System.out.println("一个用户断开了链接====="+channelContext.userid);
        Set<String> groups = channelContext.getGroups().getObj();
        /**
         * 通知管理员离线，刷新在线用户列表
         */
        ChatModel model = new ChatModel();
        model.setState(0);
        model.setSid(channelContext.userid);
        model.setSname((String) channelContext.getAttribute(Constant.KEY_USER_USER_NAME));
        bytes = createBytes(IM_CMD.USER_OFFLINE_NTY.getType(),model,null);

        //如果是后台客服断线不在线，则移除相关房间
        if(channelContext.userid.startsWith("s")){
            roomMap.remove(channelContext.userid);
        }
//        System.out.println(roomMap);

        Tio.sendToUser(channelContext.groupContext,im_room.getUserId(),WsResponse.fromBytes(bytes));
        Tio.unbindGroup(channelContext);//解除所有绑定
        Tio.remove(channelContext,"closed");

        return null;
    }

    /**
     * 所有通讯都是基于二进制发送。所以用不到此路径
     * @param wsRequest
     * @param s
     * @param channelContext
     * @return
     * @throws Exception
     */
    @Override
    public Object onText(WsRequest wsRequest, String s, ChannelContext channelContext) throws Exception {
//        System.out.println("onText");
//        System.out.println(s);
//        WsResponse wsResponse = WsResponse.fromText("用户"+channelContext.userid+"对大家说:"+s,TioWsConfig.CHARSET);
////        Tio.send(channelContext,wsResponse);
//        Tio.sendToAll(channelContext.groupContext,wsResponse);
        return null;
    }

    @Override
    public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
//        System.out.println("onBytes");
//        协议指令拆解 json格式
//        System.out.println("当前消息来自于======="+channelContext.userid);
        if(bytes!=null){
            String str = new String(bytes,WsRequest.CHARSET_NAME);
//            System.out.println("收到消息：" + str);
            String cmd = str.substring(0,TioWsConfig.CMD_LEN);
            String tmp = str.substring(TioWsConfig.CMD_LEN,str.length());
            if(str.startsWith(IM_CMD.HEARTBEAT_REQ.getType())){
                //心跳
//                System.out.println(IM_CMD.HEARTBEAT_REQ.getDescript());
                HEARTBEAT_PACKAGE.setSendTime(new Date());
                Tio.send(channelContext,WsResponse.fromBytes(createBytes(IM_CMD.HEARTBEAT_REQ.getType(),HEARTBEAT_PACKAGE,null)));
            }else if(str.startsWith(IM_CMD.P2P_CHAT_REQ.getType())){
                //私聊
//                System.out.println(IM_CMD.P2P_CHAT_REQ.getDescript());
                bytes = createBytes(IM_CMD.P2P_CHAT_NTY.getType(),objectMapper.readValue(tmp,IM_Message.class),null);
                IM_Message im_message = objectMapper.readValue(tmp,IM_Message.class);
                if(StringUtils.isNotBlank(im_message.getRid())){
                    Tio.sendToUser(channelContext.getGroupContext(),im_message.getRid(),WsResponse.fromBytes(bytes));//发给接收方
                    Tio.send(channelContext,  WsResponse.fromBytes(bytes));//发给自己提示已发送
                }
            }else if(str.startsWith(IM_CMD.JOIN_GROUP_REQ.getType())){
                //进组请求
//                System.out.println(IM_CMD.JOIN_GROUP_REQ.getDescript());
//                bytes = (IM_CMD.JOIN_GROUP_RESP.getType()+tmp).getBytes(TioWsConfig.CHARSET);
                IM_Message message = objectMapper.readValue(tmp,IM_Message.class);
                if(StringUtils.isNotBlank(message.getRid())){//如果加入的组不为空
                    if(channelContext.groupContext.groups.getGroupmap().get(message.getRid())!=null){//如果加入的组存在
                        if(!Tio.isInGroup(message.getRid(),channelContext)){
                            Tio.unbindGroup(channelContext);//清空所有绑定组
                            //如果当前绑定的组不是系统大厅；则进行绑定
                            if(message.getRid()!=im_room.getGroupId()){
                                Tio.bindGroup(channelContext,message.getRid());
                                IM_Message im_message = new IM_Message();
                                im_message.setCtx("有新用户加入群组");
                                Tio.sendToGroup(
                                        channelContext.groupContext,
                                        message.getRid(),
                                        WsResponse.fromBytes(
                                                createBytes(IM_CMD.JOIN_GROUP_RESP.getType(),im_message,null)));
                            }
                        }
                    }
                }
            }else if(str.startsWith(IM_CMD.LEAVE_GROUP_REQ.getType())){
                //离组指令
//                System.out.println(IM_CMD.LEAVE_GROUP_REQ.getDescript());
                IM_Message message = objectMapper.readValue(tmp,IM_Message.class);
                if(message.getRid()!=null){
                    Tio.unbindGroup(message.getRid(),channelContext);
                    bytes = createBytes(IM_CMD.LEAVE_GROUP_NTY.getType(),tmp,null);
                    Tio.sendToGroup(channelContext.groupContext, message.getRid(), WsResponse.fromBytes(bytes));//通知本组的所有人，当前这个离群消息
                }
            }else if(str.startsWith(IM_CMD.GROUP_CHAT_REQ.getType())){
                //群聊
//                System.out.println(IM_CMD.GROUP_CHAT_REQ.getDescript());
                IM_Message message = objectMapper.readValue(tmp,IM_Message.class);
                if(message.getRid()!=null){
                    bytes = createBytes(IM_CMD.GROUP_CHAT_NTY.getType(),tmp,null);
                    Tio.sendToGroup(channelContext.groupContext, message.getRid(), WsResponse.fromBytes(bytes));//通知本组的所有人，当前这个离群消息
                }
            }else if(str.startsWith(IM_CMD.ALL_CHAT_ROMM_REQ.getType())){
                //客服列表请求
                //轮询分配，客服。保证不要太多人挤在一个客服房间
//                System.out.println(IM_CMD.ALL_CHAT_ROMM_REQ.getDescript());
                IM_Message msg = new IM_Message();
                List<IM_Room> list = new ArrayList<>();
                for(Map.Entry<String, IM_Room> entry:roomMap.entrySet()){
                    list.add(entry.getValue());
                }
                IM_Room im_room1 = list.get(serviceIndex);
                ChatModel chatModel = new ChatModel();
                chatModel.setSname(im_room1.getNickName());
                chatModel.setSid(im_room1.getUserId());
                msg.setCtx(chatModel);
                bytes = createBytes(IM_CMD.ALL_CHAT_ROMM_RESP.getType(),msg,null);
                if(serviceIndex+1<list.size()){
                    serviceIndex++;
                }else{
                    serviceIndex=0;//重置为0
                }
                Tio.send(channelContext,  WsResponse.fromBytes(bytes));
//                List<ChatModel> chatModels = new ArrayList<>();
//                for(Map.Entry<String, IM_Room> entry:roomMap.entrySet()){
//                    ChatModel chatModel = new ChatModel();
//                    chatModel.setSname(entry.getValue().getNickName());
//                    chatModel.setSid(entry.getValue().getUserId());
//                    chatModels.add(chatModel);
//                }
//                msg.setCtx(chatModels);
//                bytes = createBytes(IM_CMD.ALL_CHAT_ROMM_RESP.getType(),msg,null);
//                Tio.send(channelContext,  WsResponse.fromBytes(bytes));

            }else if(str.startsWith(IM_CMD.MSG_TIP_REQ.getType())){
                //系统通知请求
                if (channelContext.userid.equals("s"+Constant.ROLE_ID_SUPER_ADMIN)){
                    IM_Message msg = objectMapper.readValue(tmp,IM_Message.class);
                    msg.setSid(im_room.getUserId());
                    msg.setSname(im_room.getNickName());
                    bytes = createBytes(IM_CMD.MSG_TIP_NTY.getType(),msg,null);
                    Tio.sendToAll(channelContext.groupContext,WsResponse.fromBytes(bytes));
                }else{
                    IM_Message im_message = new IM_Message();
                    im_message.setSid("s1");
                    im_message.setSname("超级管理员");
                    im_message.setCtx("警告：权限不足，发送失败");
                    bytes = createBytes(IM_CMD.MSG_TIP_NTY.getType(),im_message,null);
                    Tio.send(channelContext,WsResponse.fromBytes(bytes));
                }
            }else if(str.startsWith(IM_CMD.All_ONLINE_REQ.getType())){
                //所有客户端在线列表
                if (channelContext.userid.equals(im_room.getUserId())){
//                    Set<ChannelContext> sets = Tio.getChannelContextsByGroup(channelContext.groupContext, "c").getObj();
                    SetWithLock<ChannelContext> channels = Tio.getChannelContextsByGroup(channelContext.groupContext, "c");
                    if(channels!=null){
                        //可能为null
                        Set<ChannelContext> sets =  channels.getObj();
                        if(sets!=null){
                            Set<ChatModel> models = new HashSet<>();
                            for(ChannelContext t_ctannel:sets){
                                ChatModel t = new ChatModel();
                                t.setSname(t_ctannel.getAttribute(Constant.KEY_USER_USER_NAME).toString());
                                t.setSid(t_ctannel.userid);
                                models.add(t);
                            }
                            bytes = createBytes(IM_CMD.All_ONLINE_RESP.getType(),models,null);
                            Tio.send(channelContext,WsResponse.fromBytes(bytes));
                        }
                    }
                }
            }
        }
        return null;
    }


    public static byte [] createBytes(String cmd,Object im_message,String charset) throws UnsupportedEncodingException {
        return (cmd+JSON.toJSONString(im_message==null?new IM_Message():im_message)).getBytes(charset==null?TioWsConfig.CHARSET:charset);
    }


}
